#include <iostream>
#include <string.h>
#include <cstdlib>
#include <cstdio>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include "locker.h"
#include "thread_pool.h"
#include <signal.h>
#include "http_conn.h"

#define MAX_FD 65535
#define MAX_EVENT_NUM 10000

//添加文件描述符到epoll中
extern void addfd(int epfd, int fd, bool one_shot);
//从epoll中删除文件描述符
extern void removefd(int epfd, int fd);
extern void modfd(int epfd, int fd, int ev);

//添加信号捕捉
void addsig(int sig, void(handler)(int))
{
    struct sigaction sa;
    memset(&sa, '\0', sizeof(sa));
    sa.sa_handler = handler;
    sigfillset(&sa.sa_mask); //设置临时阻塞信号集
    sigaction(sig, &sa, NULL);
}

int main(int argc, char **argv)
{

    int port = atoi(argv[1]);
    const char *ip = argv[2];

    //对SIGPIPE做处理
    addsig(SIGPIPE, SIG_IGN);

    //初始化线程池
    threadpool<http_conn> *pool = NULL;
    try
    {
        pool = new threadpool<http_conn>();
    }
    catch (const std::exception &e)
    {
        std::cerr << e.what() << '\n';
        exit(-1);
    }

    //创建一个数组用于保存所有客户端信息

    http_conn *users = new http_conn[MAX_FD];

    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    int reuse = 1;
    //设置端口复用
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    //绑定
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    // inet_pton(AF_INET, ip, &addr.sin_addr.s_addr);
    addr.sin_addr.s_addr = INADDR_ANY;
    bind(listenfd, (struct sockaddr *)&addr, sizeof(addr));

    listen(listenfd, 5);

    //多路io复用 epollx
    epoll_event events[MAX_EVENT_NUM];

    int epfd = epoll_create(5);

    //将文件描述符添加到epoll对象中
    addfd(epfd, listenfd, false);

    http_conn::m_epfd = epfd;

    while (true)
    {
        //检测到几个事件
        int num = epoll_wait(epfd, events, MAX_EVENT_NUM, -1);
        if ((num < 0) && (errno != EINTR))
        {
            std::cout << "epoll listen failed" << std::endl;
            break;
        }

        //循环遍历事件数组
        for (int i = 0; i < num; i++)
        {

            int sockfd = events->data.fd;
            //有客户端连接进来
            if (sockfd == listenfd)
            {
                struct sockaddr_in client_addr;
                socklen_t clientaddr_len = sizeof(client_addr);

                int connfd = accept(listenfd, (struct sockaddr *)&client_addr, &clientaddr_len);
                if (http_conn::m_user_count >= MAX_FD)
                {
                    //目前连接数满了
                    //给客户端回显一个信息
                    close(connfd);
                    continue;
                }

                //将新连接的数据初始化，放到数组中
                users[connfd].init(connfd, client_addr);
            }
            else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))
            {
                //异常断开或者错误发生
                users[sockfd].close_conn();
            }
            else if (events[i].events & EPOLLIN)
            {
                //旧文件描述符，的读事件
                //一次性读所有数据
                //一次性把所有数据都读完
                if (users[sockfd].read())
                {
                    //一次性把所有数据都读完，然后交给工作线程进行业务逻辑处理
                    pool->append(users+sockfd);
                }else
                {
                    users[sockfd].close_conn();
                }
            }else if(events[i].events&EPOLLOUT)
            {
                if(!users[sockfd].write())
                {
                    users[sockfd].close_conn();
                }
            }
        }
    }


    close(epfd);
    close(listenfd);
    delete [] users;
    delete pool;

    return 0;
}